package com.cy.cy2025.util

import android.location.Address

/**
 * Android 坐标体系工具类
 * 支持三种主流坐标系转换：
 * 1. WGS84：国际标准坐标系（GPS原始坐标、原生Android定位默认）
 * 2. GCJ-02：国测局加密坐标系（高德、腾讯地图使用）
 * 3. BD-09：百度加密坐标系（百度地图使用）
 */
object CoordinateUtils {
    // 地球半长轴（WGS84椭球参数）
    private const val EARTH_SEMIMAJOR_AXIS = 6378245.0

    // 椭球偏心率平方
    private const val EARTH_ECCENTRICITY_SQUARE = 0.006693421622965943

    // 百度坐标加密偏移量
    private const val BAIDU_X_PI = Math.PI * 3000.0 / 180.0

    /**
     * 坐标模型：存储经纬度和坐标系类型
     */
    data class Coordinate(
        val latitude: Double,   // 纬度（在前）
        val longitude: Double,  // 经度（在后）
        val type: CoordinateType // 坐标系类型
    )

    /**
     * 坐标系类型枚举
     */
    enum class CoordinateType {
        WGS84,   // 国际标准
        GCJ02,   // 高德/腾讯
        BD09     // 百度
    }

    // ------------------------------ 转换方法（纬度在前，经度在后） ------------------------------
    /**
     * WGS84 转 GCJ-02（适用于将原生定位结果转为高德/腾讯地图可用坐标）
     * @param wgs84Lat WGS84纬度
     * @param wgs84Lng WGS84经度
     */
    fun wgs84ToGcj02(wgs84Lat: Double, wgs84Lng: Double): Coordinate {
        // 先校验是否在国内（国内需要加密，国外直接返回WGS84）
        if (!isLocationInChina(wgs84Lng, wgs84Lat)) {
            return Coordinate(wgs84Lat, wgs84Lng, CoordinateType.WGS84)
        }

        var dLat = transformLat(wgs84Lng - 105.0, wgs84Lat - 35.0)
        var dLng = transformLng(wgs84Lng - 105.0, wgs84Lat - 35.0)
        val radLat = wgs84Lat / 180.0 * Math.PI
        var magic = Math.sin(radLat)
        magic = 1 - EARTH_ECCENTRICITY_SQUARE * magic * magic
        val sqrtMagic = Math.sqrt(magic)

        dLat =
            (dLat * 180.0) / ((EARTH_SEMIMAJOR_AXIS * (1 - EARTH_ECCENTRICITY_SQUARE)) / (magic * sqrtMagic) * Math.PI)
        dLng = (dLng * 180.0) / (EARTH_SEMIMAJOR_AXIS / sqrtMagic * Math.cos(radLat) * Math.PI)

        val gcjLat = wgs84Lat + dLat
        val gcjLng = wgs84Lng + dLng
        return Coordinate(gcjLat, gcjLng, CoordinateType.GCJ02)
    }

    /**
     * GCJ-02 转 WGS84（适用于将高德坐标转为原生定位可用坐标）
     * @param gcj02Lat GCJ-02纬度
     * @param gcj02Lng GCJ-02经度
     */
    fun gcj02ToWgs84(gcj02Lat: Double, gcj02Lng: Double): Coordinate {
        val gcj02Coord = wgs84ToGcj02(gcj02Lat, gcj02Lng)
        val dLat = gcj02Coord.latitude - gcj02Lat
        val dLng = gcj02Coord.longitude - gcj02Lng
        val wgs84Lat = gcj02Lat - dLat
        val wgs84Lng = gcj02Lng - dLng
        return Coordinate(wgs84Lat, wgs84Lng, CoordinateType.WGS84)
    }

    /**
     * GCJ-02 转 BD-09（适用于将高德坐标转为百度地图可用坐标）
     * @param gcj02Lat GCJ-02纬度
     * @param gcj02Lng GCJ-02经度
     */
    fun gcj02ToBd09(gcj02Lat: Double, gcj02Lng: Double): Coordinate {
        val x = gcj02Lng
        val y = gcj02Lat
        val z = Math.sqrt(x * x + y * y) + 2.0 * Math.sin(y * BAIDU_X_PI)
        val theta = Math.atan2(y, x) + 3.0 * Math.cos(x * BAIDU_X_PI)
        val bdLng = z * Math.cos(theta) + 0.00002 * Math.cos(theta)
        val bdLat = z * Math.sin(theta) + 0.00002 * Math.sin(theta)
        return Coordinate(bdLat, bdLng, CoordinateType.BD09)
    }

    /**
     * BD-09 转 GCJ-02（适用于将百度坐标转为高德地图可用坐标）
     * @param bd09Lat BD-09纬度
     * @param bd09Lng BD-09经度
     */
    fun bd09ToGcj02(bd09Lat: Double, bd09Lng: Double): Coordinate {
        val x = bd09Lng - 0.0065
        val y = bd09Lat - 0.006
        val z = Math.sqrt(x * x + y * y) - 2.0 * Math.sin(y * BAIDU_X_PI)
        val theta = Math.atan2(y, x) - 3.0 * Math.cos(x * BAIDU_X_PI)
        val gcj02Lng = z * Math.cos(theta)
        val gcj02Lat = z * Math.sin(theta)
        return Coordinate(gcj02Lat, gcj02Lng, CoordinateType.GCJ02)
    }

    // ------------------------------ 辅助方法 ------------------------------
    /**
     * 校验坐标是否在中国境内（仅国内需要GCJ-02加密）
     * 中国地理范围：经度 73.66~135.05，纬度 3.86~53.55
     * @param lng 经度
     * @param lat 纬度
     */
    fun isLocationInChina(lng: Double, lat: Double): Boolean {
        return lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55
    }

    /**
     * 从 Geocoder 返回的 Address 中提取坐标，并默认标记为 GCJ-02（国内设备通用）
     * （Address对象的getLatitude()返回纬度，getLongitude()返回经度，符合参数顺序）
     */
    fun getCoordinateFromAddress(address: Address): Coordinate {
        val lat = address.latitude  // 纬度在前
        val lng = address.longitude // 经度在后
        // 国内设备默认按 GCJ-02 处理，海外设备自动按 WGS84 处理
        return if (isLocationInChina(lng, lat)) {
            Coordinate(lat, lng, CoordinateType.GCJ02)
        } else {
            Coordinate(lat, lng, CoordinateType.WGS84)
        }
    }

    // ------------------------------ 内部计算方法 ------------------------------
    private fun transformLat(x: Double, y: Double): Double {
        var ret =
            -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x))
        ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0
        ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0
        ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320.0 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0
        return ret
    }

    private fun transformLng(x: Double, y: Double): Double {
        var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x))
        ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0
        ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0
        ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x * Math.PI / 30.0)) * 2.0 / 3.0
        return ret
    }
}